#pragma once
#include <functional>
#include <mutex>
#include <string>
#include <unordered_map>

namespace cpp_component
{
template <typename Function> struct function_traits : public function_traits<decltype(&Function::operator())>
{};

template <typename ClassType, typename ReturnType, typename... Args> struct function_traits<ReturnType (ClassType::*)(Args...) const>
{
    typedef ReturnType (*pointer)(Args...);
    typedef std::function<ReturnType(Args...)> function;
};

template <typename Function> typename function_traits<Function>::function to_function(Function &lambda)
{
    return static_cast<typename function_traits<Function>::function>(lambda);
}

template <typename Lambda> size_t getAddress(Lambda lambda)
{
    auto function = new decltype(to_function(lambda))(to_function(lambda));
    void *func = static_cast<void *>(function);
    return (size_t)func;
}

template <typename Clazz, typename Return, typename... Arguments> size_t getMemberAddress(std::function<Return (Clazz::*)(Arguments...)> &executor)
{
    typedef Return (Clazz::*fnType)(Arguments...);
    fnType **fnPointer = executor.template target<fnType *>();
    if (fnPointer != nullptr)
    {
        return (size_t)*fnPointer;
    }
    return 0;
}

template <typename T> class event
{
   public:
    event(){};
    ~event(){};
    using handler = typename std::unordered_multimap<std::string, std::function<T>>::iterator;
    template <typename... Args> void notify(const std::string &key, Args &&... args)
    {
        auto subscriber = m_subscribers.equal_range(key);

        if (subscriber.first != std::end(m_subscribers))
        {
            for (auto iter = subscriber.first; iter != subscriber.second; ++iter)
            {
                iter->second(std::forward<Args>(args)...);
            }
        }
    }

    handler subscribe(const std::string &key, const std::function<T> function)
    {
        std::cout << function.target_type().name() << std::endl;
        std::cout << getAddress(function) << std::endl;
        std::lock_guard<std::mutex> lock(m_mutex);
        return m_subscribers.emplace(key, function);
    }

    void unsubscribe(handler &iter)
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (iter != m_subscribers.end())
        {
            m_subscribers.erase(iter);
            iter = m_subscribers.end();
        }
    }
    void unsubscribe(const std::string &key, const std::function<T> function) {}

   private:
    std::mutex m_mutex;
    std::unordered_multimap<std::string, std::function<T>> m_subscribers;
};
}  // namespace cpp_component
